home *** CD-ROM | disk | FTP | other *** search
/ PC Media 20 / PC MEDIA CD20.iso / share / prog / cursoasm / cap7.msg < prev    next >
Text File  |  1993-07-16  |  18KB  |  291 lines

  1.            INTRODUCCION AL ASM: USO DE LOS PROGRAMAS ENSAMBLADORES
  2.            =======================================================
  3.  
  4.    Como anticipamos en el anterior capítulo, comenzaremos aquí con el uso de
  5. los ensambladores comerciales. Después de algo de teoría, veremos el famoso
  6. 'Hello, world' tal y como quedaría en lenguaje ensamblador.
  7.  
  8.    Para entender bien todo el proceso desde el fichero fuente, que suele llevar
  9. la extensión '.ASM', hasta el programa que reside en memoria una vez que el DOS
  10. lo ha cargado, haremos el camino de atrás a delante, es decir, comenzando en el
  11. programa final y acabando en el '.ASM'.
  12.  
  13.    Un programa en memoria, una vez cargado, suele consistir en un bloque de
  14. bytes (desde unas decenas de bytes hasta algunos cientos de kilobytes), en el
  15. que residen tanto el código como los datos y la pila del programa. Ya que la
  16. longitud total puede ser superior a 64 KB, no se puede usar un solo valor de
  17. segmento para acceder a todo el código y a todo los datos del programa. Para
  18. posibilitar el acceso a todo el bloque de programa, este bloque está dividido
  19. en varios bloques distintos, normalmente alineados para comenzar en direcciones
  20. múltiplos de 16d. A estos bloques lógicos (ya que la división no es física), se
  21. les denomina 'segmentos'; no hay que confundir este término con los 'segmentos'
  22. que vimos al ver la arquitectura segmentada de los 80x86: aquellos 'segmentos'
  23. eran siempre de 64K bytes, mientras que estos 'segmentos' pueden tener cual-
  24. quier longitud menor o igual a 64K bytes.
  25.  
  26.    Estos bloques lógicos o segmentos se clasifican en función de lo que contie-
  27. nen. Principalmente, pueden contener código, datos, o constituir espacio para
  28. la pila. Por tanto, se habla de 'segmentos de datos', 'segmentos de código', y
  29. 'segmento de pila' (no suele haber más que un segmento de pila).
  30.  
  31.    Durante la ejecución del programa, CS suele contener un valor de forma que
  32. CS:0 sea el primer byte de uno de los segmentos de código, mientras que DS
  33. suele contener un valor de manera que DS:0 sea el primer byte de uno de los
  34. segmentos de datos. SS se suele mantener inalterado a lo largo del programa,
  35. apuntando al segmento de pila.
  36.  
  37.    Para saltar de un punto del programa a otro que resida en otro segmento, es
  38. necesario modificar tanto CS como IP, mientras que si el destino reside en el
  39. mismo segmento que la instrucción de salto basta con modificar IP. De la misma
  40. forma, para acceder a una variable que resida en el segmento de datos apuntado
  41. por DS no es necesario modificar DS, mientras que si la variable reside en otro
  42. segmento de datos es necesario cargar DS o ES para que apunte al principio del
  43. otro segmento antes de acceder a la variable. Como podemos intuir, el acceso
  44. a código y datos 'cercanos' (que residan en el CS y DS actuales) es mucho más
  45. cómodo, además de ligeramente más rápido, que el acceso a código y datos leja-
  46. nos (en otros segmentos que los apuntados por CS y DS).
  47.  
  48.    Más adelante volveremos a este galimatías de segmentos y veremos lo que son
  49. los famosos 'modelos' estándar de memoria. Por ahora, dejemos este tema y siga-
  50. mos.
  51.  
  52.    Esta imagen de memoria la crea el DOS a partir del fichero '.EXE' que el
  53. usuario ha ordenado ejecutar (más adelante veremos las peculiaridades de los
  54. '.COM'). Lo que el DOS hace no es símplemente cargar el '.EXE' tal y como está
  55. en el disco y saltar a la primera dirección del '.EXE', sino que el proceso de
  56. carga es bastante complejo. En realidad, el '.EXE' es el bloque que hemos
  57. visto, compuesto de varios bloques lógicos (llamados segmentos), y precedido de
  58. una cabecera de longitud variable. El estudio detallado de esta cabecera queda
  59. fuera de nuestro ámbito, pero nos interesa saber el contenido aproximado.
  60.  
  61.    Los primeros dos bytes del '.EXE' son la cadena 'MZ', que lo identifican
  62. como '.EXE'. La cabecera indica, además, su propia longitud, un CRC para el
  63. fichero completo (es decir, una suma utilizada para verificar la integridad del
  64. fichero), el punto - dentro del bloque de código y datos - al que hay que sal-
  65. tar (el punto de entrada al programa), los valores iniciales de SS y SP para
  66. apuntar a la pila, y algún detalle más. Pero, además de todo esto, lo más
  67. importante de la cabecera es la llamada 'tabla de reubicación'.
  68.  
  69.    En un sistema MSDOS, los programas deben ser capaces de correr en cualquier
  70. punto de la memoria. Las instrucciones de los procesadores 80x86 son en muchos
  71. casos relativas (es decir, un salto suele ser 'salta 10 bytes más adelante' y
  72. no 'salta a la dirección 310'), con lo que gran parte del código funciona en
  73. cualquier punto de la memoria. Pero existen algunas instrucciones que utilizan
  74. direcciones absolutas, como por ejemplo cargar en un registro la dirección
  75. completa de una variable (offset y segmento). Estas instrucciones tienen que
  76. ser distintas cuando los datos están en la zona baja de la memoria o en la zona
  77. alta. Para solucionar este problema, un '.EXE' lleva en la cabecera una tabla
  78. en la que se indican todas las instrucciones que necesitan ser modificadas al
  79. cargar el programa.
  80.  
  81.    Por tanto, el proceso de carga es el siguiente (realizado por el MSDOS):
  82.  
  83.             - Carga del '.EXE' en dos partes: cabecera y bloque de programa.
  84.             - Recorrido de la tabla de reubicación, arreglando cada referencia
  85.               marcada en la tabla.
  86.             - Inicialización de los registros de segmento, tal y como viene
  87.               especificado en la cabecera, y salto al punto de entrada.
  88.  
  89.    Hay que especificar dos cosas en es punto, antes de pasar a los ficheros
  90. '.OBJ': la primera, que todas las referencias de la tabla de reubicación no son
  91. para instrucciones, sino que algunas pueden ser para datos. Esto ocurre en el
  92. caso de que una variable estática, al ser declarada, sea inicializada con un
  93. valor que dependa de la situación el memoria de otra variable. La segunda pun-
  94. tualización es que el MSDOS se encarga de inicializar algunos registros de seg-
  95. mento antes de entrar al programa, pero no todos. CS, evidentemente, apunta al
  96. segmento de código que contien el punto de entrada. SS apunta ya al segmento de
  97. pila. Pero DS, en principio, no apunta a ningún sitio en concreto. Por ello,
  98. las primeras instrucciones de un programa ASM suelen ser para inicializar DS
  99. apuntando al segmento de datos que vayamos a usar.
  100.  
  101.    Un fichero '.EXE' como los que hemos visto lo construye un programa que se
  102. incluye con cualquier compilador o ensamblador, llamado enlazador o LINKer (en
  103. spanglish). Los linkers de Microsoft suelen llamarse LINK.EXE, mientras que los
  104. de Borland se llaman siempre TLINK.EXE.
  105.  
  106.    Un linker toma como entrada un conjunto de uno o varios ficheros llamados
  107. 'ficheros objeto', y genera como salida un fichero '.EXE' (opcionalmente, el
  108. TLINK puede generar un '.COM', mientras que con el LINK es necesario convertir
  109. el '.EXE' resultante en '.COM' con la utilidad EXE2BIN). Los ficheros objeto
  110. tienen extensión '.OBJ', y son ficheros generados directamente por los ensam-
  111. bladores y compiladores a partir de nuestros listados con el código fuente.
  112.  
  113.    Un fichero '.OBJ' contiene varios bloques, que son los que se usan luego
  114. para construir los segmentos que van en el '.EXE'. Además de estos bloques, los
  115. ficheros '.OBJ' llevan también algunas referencias que luego deberán ser in-
  116. cluidas en la tabla de reubicación del '.EXE'. Pero lo que diferencia princi-
  117. palmente a un '.OBJ' de un '.EXE' (aparte del formato de fichero), son algo
  118. llamado 'referencias externas' y 'declaraciones públicas'.
  119.  
  120.    Para facilitar el trabajo del programador, los lenguajes actuales permiten
  121. que un programa '.EXE' se construya a partir de código de varios listados
  122. diferentes. Además, cada uno de estos 'módulos' (que así se llaman) puede estar
  123. escrito en un lenguaje diferente, siempre que tengamos un compilador para ese
  124. lenguaje que genere ficheros '.OBJ' estándar. Una de las complicaciones que
  125. esta posibilidad añade, es que es necesario poder acceder desde un módulo a
  126. funciones y variables declaradas en otro módulo. Lo que se hace es permitir a
  127. cualquier módulo hacer declaraciones externas, en las que se indica al compila-
  128. dor que una variable o función está en algún otro módulo con el que luego
  129. linkaremos éste. Así, el '.OBJ' generado incluye precisamente una referencia
  130. externa con el nombre de la función o variable. El módulo que define una varia-
  131. ble o función a la que se quiere acceder desde otro módulo debe declarar a ésta
  132. como 'pública', de manera que el '.OBJ' generado lleve una pequeña indicación
  133. advirtiendo que la variable de ese nombre está en ese módulo, junto con la
  134. dirección de ésta. El linker es el que se encarga de resolver todas estas refe-
  135. rencias, modificar todas las instrucciones que sea posible en este punto y
  136. dejar las que no se puedan arreglar en la tabla de reubicación del '.EXE'.
  137.  
  138.    Otra de las cosas que hace el linker es 'unir' los segmentos de distintos
  139. módulos en uno sólo. Ya que cada segmento del '.OBJ' va acompañado de un nombre
  140. y de algunos datos, si estos coinciden el linker supone que se ha hecho así
  141. intencionadamente y los une en uno sólo, poniendo uno a continuación del otro.
  142. Las reglas que sigue el linker para reconocer los segmentos y actuar en conse-
  143. cuencia  son, junto con el eslabón perdido, uno de los misterios más ignotos de
  144. la humanidad. Ahora en serio, las reglas que siguen en LINK y el TLINK para
  145. reconocer el segmento de pila, etc... son ligeramente distintas, pero existe
  146. una manera de hacer todo esto sin preocuparse de los nombres de los segmentos,
  147. y, por ejemplo, linkar un módulo ASM con uno escrito en C y conseguir fundir
  148. los segmentos de código y datos de los dos módulos. Para esto usaremos carácte-
  149. rísticas del MASM 5.0 y superiores, y del TASM 1.0 y superiores, por lo que os
  150. recomiendo que consigáis al menos estas versiones de los ensambladores (con uno
  151. de los dos vale).
  152.  
  153.    Además de los ficheros '.OBJ', un linker puede tambier linkar ficheros
  154. '.LIB'. Un '.LIB' o fichero de librería es un fichero que contiene un conjunto
  155. de ficheros '.OBJ', uno detrás de otro, que por alguna razón se suelen usar
  156. conjuntamente. Además, el '.LIB' lleva un índice hasheado para encontrar rápi-
  157. damente las referencias a símbolos del '.LIB'. Cuando el linker encuentra una
  158. referencia a un símbolo externo, primero lo busca en los '.OBJ' especificados
  159. por el usuario, y en caso de no encontrarlo busca en los índices de las '.LIB'
  160. especificadas. Si lo encuentra, extrae el '.OBJ' de la '.LIB' en el que apare-
  161. ce el símbolo (variable o función) y lo añade al '.EXE' generado. Por ejemplo,
  162. todas las funciones que un compilador de C provee al programador vienen en una
  163. librería (llamada librería estándar). Es bastante sencillo extraer de un '.LIB'
  164. los '.OBJ' que lo componen, aunque la mayoría de los gestores de librería no lo
  165. permiten.
  166.  
  167.    Ahora que ya sabemos cómo son los '.OBJ', el estudio del listado fuente de
  168. programa en ASM resulta bastante sencillo: un listado fuente en lenguaje ensam-
  169. blador es prácticamente una imagen del '.OBJ' que se quiere generar. El
  170. listado está dividido en fragmentos, denominados 'segmentos', que son los blo-
  171. ques que el '.OBJ' lleva para el linker. Estos segmentos pueden ser de código
  172. o de datos. Además, podemos encontrar declaraciones PUBLIC (símbolos que se
  173. exportan en el '.OBJ' para uso y disfrute generalizado de los demás módulos);
  174. podemos también encontrar declaraciones EXTRN (símbolos que se indican en el
  175. '.OBJ' al linker para ser buscados entre los demás módulos).
  176.  
  177.    Veamos ya cómo quedaría en ASM el famoso 'Hello, world!':
  178.  
  179. =================8<============================8<===========================
  180. PILA    SEGMENT STACK 'STACK'    ; Abre el segmento de pila
  181.     DW 100h DUP (?)                ; 200h (512d) bytes de pila
  182. PILA    ENDS                    ; Cierra el segmento de pila
  183.  
  184. DATOS    SEGMENT 'DATA'            ; Abre el segmento de datos
  185. Msg    DB 'Hello, world!$'            ; Mensaje a imprimir
  186. DATOS    ENDS                    ; Cierra el segmento de datos
  187.  
  188. CODIGO    SEGMENT 'CODE'            ; Abre el segmento de código
  189.     ASSUME CS:CODIGO, DS:DATOS, SS:PILA
  190. Entrada    PROC                    ; Abre el procedimiento 'Entrada'
  191.     mov    ax,DATOS                ; Valor de segmento para 'DATOS'
  192.     mov    ds,ax                    ; Para acceder a 'Msg'
  193.     mov    dx,OFFSET Msg            ; Para la int 21h, servicio 9
  194.     mov    ah,9                    ; Especifica servicio9
  195.     int    21h                        ; Invoca servicio 9: imprimir cadena
  196.     mov    ax,4C00h                ; Servicio 4Ch, valor de retorno 0
  197.     int    21h                        ; Invoca servicio 4Ch: retorno al DOS
  198. Entrada    ENDP                    ; Cierra el procedimiento 'Entrada'
  199. CODIGO    ENDS                    ; Cierra el segmento 'CODIGO'
  200.  
  201.     END Entrada                    ; Fin del programa, punto de entrada 'Entrada'
  202. =================8<============================8<===========================
  203.  
  204.    Los ensambladores más modernos permiten una manera más sencilla de escribir
  205. este programa, usando las directivas de segmento simplificadas, pero creo que
  206. es mejor ver primero la forma 'clásica' y luego la simplificada, con lo que la
  207. comprensión es mucho mayor.
  208.  
  209.    Como vemos, el listado está dividido en tres partes principales, que corres-
  210. ponden directamente a los segmentos del '.OBJ'. El principio de un segmento se
  211. indica con la directiva del ensamblador 'SEGMENT', y el final con la directiva
  212. 'ENDS'. Hay que tener en cuenta que estas directivas no son instrucciones en
  213. ASM, sino que indican al ensamblador cómo debe realizar su trabajo, por lo que
  214. no generan código. Al final del listado siempre debe aparecer la directiva END,
  215. que indica el final del listado. Opcionalmente, puede llevar un argumento cuyo
  216. significado veremos más tarde.
  217.  
  218.    El trabajo del ensamblador es básicamente el siguiente: va leyendo líneas
  219. del fichero '.ASM', que serán siempre directivas. Cuando encuentra una línea
  220. con la directiva SEGMENT, prepara un buffer para ir introduciendo en él los
  221. datos que irá leyendo. Inicializa un contador interno a 0, para apuntar al
  222. principio de buffer. Entonces, comienza a procesar las líneas de otra forma
  223. hasta que encuentra un ENDS, que indica el final del segmento. El contenido del
  224. buffer irá al '.OBJ' prácticamente tal y como está. Las líneas interiores a un
  225. segmento (entre un SEGMENT y su ENDS correspondiente) se procesan de otra
  226. forma. Aunque algunas pueden ser directivas, la mayoría están destinadas a ge-
  227. nerar código o datos que irán en el '.OBJ'. Después de almacenar los bytes que
  228. una línea define en el buffer, el contador interno se incrementa para apuntar
  229. a los siguientes bytes libres. Estas líneas pueden ser líneas de código o líne-
  230. as de datos. Si son de código, la sintáxis será la siguiente:
  231.  
  232.         ETIQUETA: INSTRUCCION OPERANDO(S) ;COMENTARIO
  233.  
  234.    Mientras que si son líneas de datos, la sintáxis será la siguiente:
  235.  
  236.         ETIQUETA D<X> DATOS ;COMENTARIO
  237.  
  238.    En esta línea, <X> se sustituye por una letra que indica el tamaño de los
  239. datos (B: bytes, W: palabras, D:dobles palabras, ...).
  240.  
  241.    La etiqueta es un símbolo, en principio único (no puede haber dos etiquetas
  242. iguales en un mismo listado), que el ensamblador almacena en una tabla de sím-
  243. bolos interna junto con el valor del contador interno al procesar la línea.
  244. También se almacena el contexto en el que aparece la etiqueta (si es de datos
  245. o de código, el tamaño de los datos a los que apunta, el segmento al que perte-
  246. nece...). Se utiliza para referenciar desde otro punto una variable por nombre,
  247. en caso de que se trate de una etiqueta de datos, o para saltar a un punto
  248. determinado desde otro lugar del programa.
  249.  
  250.    La instrucción y los operandos son cualquier instrucción válida del 80x86, y
  251. el comentario puede ser cualquier cosa que aclare la instrucción comentada. Los
  252. datos de una línea del segundo tipo pueden ser números separados por comas,
  253. en cuyo caso se sitúan uno detrás del siguiente. Cuando no se quiere iniciali-
  254. zar una variable, se puede poner un símbolo de interrogación ('?'). Si se quie-
  255. re repetir varias veces una secuencia, se puede utilizar la construcción:
  256.  
  257.         n DUP (dato, dato, dato)
  258.  
  259.    Donde 'n' es un número que representa cuántas veces se debe repetir la se-
  260. cuencia.
  261.  
  262.    Estudiemos ahora con detalle el listado HELLO.ASM. Para hacer la prueba, se
  263. puede recortar el listado y salvarlo como HELLO.ASM. Después, podemos ensamblar
  264. con 'TASM HELLO' o con 'MASM HELLO;'. El linkado lo haremos con 'TLINK HELLO' o
  265. 'LINK HELLO;', tras lo cual tendremos el HELLO.EXE en el directorio actual. La
  266. sintáxis de la línea de comandos varía ligeramente de una versión a otra del
  267. mismo compilador, por lo que os recomiendo consultar el manual o hacer algunas
  268. pruebas.
  269.  
  270.    Las primeras tres líneas del listado proporcionan una segmento para la pila
  271. del programa. Como vemos, el segmento lleva un nombre que le proporcionamos
  272. nosotros, en este caso el nombre 'PILA'. Este nombre es cualquier identificador
  273. válido en ASM; recordemos que el ensamblador no distingue entre mayúsculas y
  274. minúsculas. Después de la directiva SEGMENT van varios parámetros opcionales.
  275. Ya que normalmente usaremos las directivas de segmento simplificadas, no entra-
  276. remos a detallar estos parámetros. Si alguien tiene mucho interés puede consul-
  277. tar el manual del ensamblador o la ayuda online de éste, en caso de que tenga.
  278. Como se aprecia, se dejan 100h (256d) palabras sin inicializar para la pila.
  279. Esto suele ser suficiente para programas que no usen la pila intensivamente.
  280.  
  281.    Las siguientes tres líneas definen otro segmento, que lleva los datos del
  282. programa. En este caso, la única variable es una cadena con el mensaje a impri-
  283. mir. Cuando tras un DB aparece una cadena entre comillas simples, el ensambla-
  284. dor introduce todos los caracteres, uno detras de otro.
  285.  
  286.    Para no hacer el mensaje demasiado largo, continuaremos el estudio de éste
  287. listado en el siguiente capítulo.
  288.  
  289.    Salut :-)
  290.  
  291.    Jon